//
//  TCPSendDataManager.swift
//  TimeBoat
//
//  Created by Jun on 2022/9/8.
//  Copyright © 2022 Time Boat. All rights reserved.
//

import Foundation


@objcMembers class TCPSendDataManager: NSObject {
    @objc static let shared = TCPSendDataManager()
    
    /// 数据队列
    private let SYNC_OBJECT = UUID.init()
    private var mCMDDataList = [Data]()
    private let mConditionValue: Int = 1
    private let mLock: NSConditionLock = NSConditionLock.init()
    private var mThread: Thread?
    private var mLKTimer: Timer?
    private let mDispatchQueue = DispatchQueue.init(label: "tcp-send-cmd-queue")
    private let TAG = LogTag.init(rawValue: "TCPSendDataManager")
    
    override init() {
        super.init()
        
        createThread()
        createLKTimer()
        
    }
    
    @objc public func start() {
        Log.debug(tag: TAG, "start")
        sendLKCommand()
    }
    
    /// 外部调用发送命令
    public func sendCommand(_ cmdData: Data?) {
        if nil != cmdData {
            mDispatchQueue.async {
                self.pushCMDData(cmdData: cmdData!)
            }
        }
    }
    
    private func pushCMDData(cmdData: Data) {
        objc_sync_enter(SYNC_OBJECT)
        mCMDDataList.append(cmdData)
        releaseLock()
        objc_sync_exit(SYNC_OBJECT)
    }
    
    private func popCMDData() -> Data? {
        var item: Data?
        objc_sync_enter(SYNC_OBJECT)
        if mCMDDataList.count > 0 {
            item = mCMDDataList.removeFirst()
        }
        
        objc_sync_exit(SYNC_OBJECT)
        
        return item
    }
    
    private func waitLock() {
        mLock.lock(whenCondition: mConditionValue)
    }
    
    private func releaseLock() {
        mLock.unlock(withCondition: mConditionValue)
    }
    
    private func createThread() {
        if (nil == mThread) {
            mThread = Thread.init(target: self, selector: #selector(threadHandle), object: nil)
            mThread!.start()
        }
    }
    
    @objc func threadHandle() {
        var item: Data?
        while (true) {
            autoreleasepool {
                item = popCMDData()
                if (nil != item) {
                    TCPBaseManager.shared.writeCommandData(data: item)
                } else {
                    waitLock()
                }
            }
        }
    }
    
    // 定时心跳
    private func createLKTimer() {
        if nil == mLKTimer {
            let interval = TimeInterval(ConfigManager.shared.getHeartBeatTimeInterval())
            
            mLKTimer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(sendLKCommand), userInfo: nil, repeats: true)
            RunLoop.main.add(mLKTimer!, forMode: .common)
        }
    }
    
    private func getIMEI() -> String? {
        return AccountManager.shareAccount().deviceImei()
    }
    
    private func getUserUUID() -> String? {
        return AccountManager.shareAccount().userUUID()
    }
    
    @objc func sendLKCommand() {
        Log.debug(tag: TAG, "sendLKCommand")
        sendCommand(TCPCMDType.buildLKCommand(imei: getIMEI(), userUUID: getUserUUID()))
    }
    
    public func sendCoordinateCommand(deviceTimestampSecond: UInt32, lng: String, lat: String) {
        sendCommand(TCPCMDType.buildCoordinateCommand(imei: getIMEI(),
                                                      userUUID: getUserUUID(),
                                                      deviceTimestampSecond: deviceTimestampSecond,
                                                      lng: lng,
                                                      lat: lat))
    }
    
    public func sendStepCommand(deviceTimestampSecond: UInt32, stepCount: UInt32, distance: UInt32, cal: UInt32) {
        sendCommand(TCPCMDType.buildStepCommand(imei: getIMEI(),
                                                userUUID: getUserUUID(),
                                                deviceTimestampSecond: deviceTimestampSecond,
                                                stepCount: stepCount,
                                                distance: distance,
                                                cal: cal))
    }
    
    public func sendHeartRateCommand(deviceTimestampSecond: UInt32, heartRate: UInt32) {
        sendCommand(TCPCMDType.buildHeartRateCommand(imei: getIMEI(),
                                                     userUUID: getUserUUID(),
                                                     deviceTimestampSecond: deviceTimestampSecond,
                                                     heartRate: heartRate))
    }
    
    public func sendTempCommand(deviceTimestampSecond: UInt32, temp: String?) {
        sendCommand(TCPCMDType.buildTempCommand(imei: getIMEI(),
                                                userUUID: getUserUUID(),
                                                deviceTimestampSecond: deviceTimestampSecond,
                                                temp: temp))
    }
    
    public func sendTotalStepCommand(deviceTimestampSecond: UInt32, stepCount: UInt32, distance: UInt32, cal: UInt32) {
        sendCommand(TCPCMDType.buildTotalStepCommand(imei: getIMEI(),
                                                     userUUID: getUserUUID(),
                                                     deviceTimestampSecond: deviceTimestampSecond,
                                                     stepCount: stepCount,
                                                     distance: distance,
                                                     cal: cal))
    }
    
    public func sendOxygenCommand(deviceTimestampSecond: UInt32, bloodOxygen: UInt32) {
        sendCommand(TCPCMDType.buildOxygenCommand(imei: getIMEI(),
                                                  userUUID: getUserUUID(),
                                                  deviceTimestampSecond: deviceTimestampSecond,
                                                  bloodOxygen: bloodOxygen))
    }
    
    public func sendSleepCommand(deviceTimestampSecond: UInt32, hour: UInt32, minute: UInt32, state: UInt32) {
        sendCommand(TCPCMDType.buildSleepCommand(imei: getIMEI(),
                                                 userUUID: getUserUUID(),
                                                 deviceTimestampSecond: deviceTimestampSecond,
                                                 hour: hour,
                                                 minute: minute,
                                                 state: state))
    }
    
    public func sendRunCommand(deviceTimestampSecond: UInt32, model: RunDataModel?) {
        if (nil != model) {
            sendCommand(TCPCMDType.buildRunCommand(imei: getIMEI(),
                                                   userUUID: getUserUUID(),
                                                   deviceTimestampSecond: deviceTimestampSecond,
                                                   model: model!))
        }
    }
    
    public func sendHikingCommand(deviceTimestampSecond: UInt32, model: HikingDataModel?) {
        if (nil != model) {
            sendCommand(TCPCMDType.buildHikingCommand(imei: getIMEI(),
                                                      userUUID: getUserUUID(),
                                                      deviceTimestampSecond: deviceTimestampSecond,
                                                      model: model!))
        }
    }
    
    public func sendRideCommand(deviceTimestampSecond: UInt32, model: RideDataModel?) {
        if (nil != model) {
            sendCommand(TCPCMDType.buildRideCommand(imei: getIMEI(),
                                                    userUUID: getUserUUID(),
                                                    deviceTimestampSecond: deviceTimestampSecond,
                                                    model: model!))
        }
    }
    
    public func sendMountaineeringCommand(deviceTimestampSecond: UInt32, model: MountaineeringDataModel?) {
        if (nil != model) {
            sendCommand(TCPCMDType.buildMountaineeringCommand(imei: getIMEI(),
                                                              userUUID: getUserUUID(),
                                                              deviceTimestampSecond: deviceTimestampSecond,
                                                              model: model!))
        }
    }
    
    public func sendRunningMachineCommand(deviceTimestampSecond: UInt32, model: RunningMachineDataModel?) {
        if (nil != model) {
            sendCommand(TCPCMDType.buildRunningMachineCommand(imei: getIMEI(),
                                                              userUUID: getUserUUID(),
                                                              deviceTimestampSecond: deviceTimestampSecond,
                                                              model: model!))
        }
    }
    
    public func sendBadmintonCommand(deviceTimestampSecond: UInt32, model: BallDataModel?) {
        if (nil != model) {
            sendCommand(TCPCMDType.buildBadmintonCommand(imei: getIMEI(),
                                                         userUUID: getUserUUID(),
                                                         deviceTimestampSecond: deviceTimestampSecond,
                                                         model: model!))
        }
    }
    
    public func sendBasketballCommand(deviceTimestampSecond: UInt32, model: BallDataModel?) {
        if (nil != model) {
            sendCommand(TCPCMDType.buildBasketballCommand(imei: getIMEI(),
                                                          userUUID: getUserUUID(),
                                                          deviceTimestampSecond: deviceTimestampSecond,
                                                          model: model!))
        }
    }
    
    public func sendFootballCommand(deviceTimestampSecond: UInt32, model: BallDataModel?) {
        if (nil != model) {
            sendCommand(TCPCMDType.buildFootballCommand(imei: getIMEI(),
                                                        userUUID: getUserUUID(),
                                                        deviceTimestampSecond: deviceTimestampSecond,
                                                        model: model!))
        }
    }
}
